using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows;
using System.Windows.Input;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Threading;
using Drawing = System.Drawing;
using Forms = System.Windows.Forms;

namespace Gildor.SimpleHostMonitor.Desktop.Controls
{
    /// <summary>
    /// Represents a thin wrapper for <see cref="Forms.NotifyIcon"/>
    /// </summary>
    [ContentProperty ("Text")]
    [DefaultEvent ("MouseDoubleClick")]
    public class SystemTrayIcon : FrameworkElement
    {
        Forms.NotifyIcon _notifyIcon;

        public static readonly RoutedEvent MouseClickEvent = EventManager.RegisterRoutedEvent (
            "MouseClick", RoutingStrategy.Bubble, typeof (MouseButtonEventHandler), typeof (SystemTrayIcon));

        public static readonly RoutedEvent MouseDoubleClickEvent = EventManager.RegisterRoutedEvent (
            "MouseDoubleClick", RoutingStrategy.Bubble, typeof (MouseButtonEventHandler), typeof (SystemTrayIcon));

        public static readonly RoutedEvent BalloonTipClickedEvent = EventManager.RegisterRoutedEvent (
            "BalloonTipClicked", RoutingStrategy.Bubble, typeof (EventHandler), typeof (SystemTrayIcon));

        public static readonly DependencyProperty BalloonTipTitleProperty =
            DependencyProperty.Register ("BalloonTipTitle", typeof (string), typeof (SystemTrayIcon));

        public static readonly DependencyProperty BalloonTipTextFormatProperty =
            DependencyProperty.Register ("BalloonTipTextFormat", typeof (string), typeof (SystemTrayIcon));

        public static readonly DependencyProperty BalloonTipTimeoutProperty =
            DependencyProperty.Register ("BalloonTipTimeout", typeof (int), typeof (SystemTrayIcon));

        public static readonly DependencyProperty IconProperty =
            DependencyProperty.Register ("Icon", typeof (ImageSource), typeof (SystemTrayIcon));

        public static readonly DependencyProperty TextProperty =
            DependencyProperty.Register ("Text", typeof (string), typeof (SystemTrayIcon));

        public static readonly DependencyProperty FormsContextMenuProperty =
            DependencyProperty.Register ("MenuItems", typeof (List<Forms.MenuItem>), typeof (SystemTrayIcon), new PropertyMetadata (new List<Forms.MenuItem> ()));

        protected override void OnInitialized (EventArgs e)
        {
            base.OnInitialized (e);

            if (0 == BalloonTipTimeout)
            {
                BalloonTipTimeout = 2000;
            }

            // Create and initialize the window forms notify icon based
            _notifyIcon = new Forms.NotifyIcon ();
            _notifyIcon.Text = Text;
            if (!DesignerProperties.GetIsInDesignMode (this))
            {
                _notifyIcon.Icon = FromImageSource (Icon);
            }
            _notifyIcon.Visible = FromVisibility (Visibility);

            if (this.MenuItems != null && this.MenuItems.Count > 0)
            {
                _notifyIcon.ContextMenu = new System.Windows.Forms.ContextMenu (this.MenuItems.ToArray ());
            }

            _notifyIcon.MouseDown += OnMouseDown;
            _notifyIcon.MouseUp += OnMouseUp;
            _notifyIcon.MouseClick += OnMouseClick;
            _notifyIcon.MouseDoubleClick += OnMouseDoubleClick;
            _notifyIcon.BalloonTipClicked += OnBalloonTipClicked;

            Dispatcher.ShutdownStarted += OnDispatcherShutdownStarted;
        }

        /// <summary>
        /// Called when [dispatcher shutdown started].
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void OnDispatcherShutdownStarted (object sender, EventArgs e)
        {
            _notifyIcon.Dispose ();
        }

        private void OnMouseDown (object sender, Forms.MouseEventArgs e)
        {
            OnRaiseEvent (MouseDownEvent, new MouseButtonEventArgs (
                InputManager.Current.PrimaryMouseDevice, 0, ToMouseButton (e.Button)));
        }

        private void OnMouseUp (object sender, Forms.MouseEventArgs e)
        {
            OnRaiseEvent (MouseUpEvent, new MouseButtonEventArgs (
                InputManager.Current.PrimaryMouseDevice, 0, ToMouseButton (e.Button)));
        }

        private void OnMouseDoubleClick (object sender, Forms.MouseEventArgs e)
        {
            OnRaiseEvent (MouseDoubleClickEvent, new MouseButtonEventArgs (
                InputManager.Current.PrimaryMouseDevice, 0, ToMouseButton (e.Button)));
        }

        private void OnMouseClick (object sender, Forms.MouseEventArgs e)
        {
            OnRaiseEvent (MouseClickEvent, new MouseButtonEventArgs (
                InputManager.Current.PrimaryMouseDevice, 0, ToMouseButton (e.Button)));
        }

        private void OnBalloonTipClicked (object sender, EventArgs e)
        {
            OnRaiseEvent (BalloonTipClickedEvent, new RoutedEventArgs ());
        }

        private void OnRaiseEvent (RoutedEvent handler, RoutedEventArgs e)
        {
            e.RoutedEvent = handler;
            RaiseEvent (e);
        }

        /// <summary>
        /// Gets or sets the menu items.
        /// </summary>
        /// <value>The menu items.</value>
        public List<Forms.MenuItem> MenuItems
        {
            get { return (List<Forms.MenuItem>)GetValue (FormsContextMenuProperty); }
            set { SetValue (FormsContextMenuProperty, value); }
        }

        /// <summary>
        /// Gets or sets the icon.
        /// </summary>
        /// <value>The icon.</value>
        public ImageSource Icon
        {
            get { return (ImageSource)GetValue (IconProperty); }
            set { SetValue (IconProperty, value); }
        }

        /// <summary>
        /// Gets or sets the text.
        /// </summary>
        /// <value>The text.</value>
        public string Text
        {
            get { return (string)GetValue (TextProperty); }
            set { SetValue (TextProperty, value); }
        }

        /// <summary>
        /// Occurs when [mouse click].
        /// </summary>
        public event MouseButtonEventHandler MouseClick
        {
            add { AddHandler (MouseClickEvent, value); }
            remove { RemoveHandler (MouseClickEvent, value); }
        }

        /// <summary>
        /// Occurs when [mouse double click].
        /// </summary>
        public event MouseButtonEventHandler MouseDoubleClick
        {
            add { AddHandler (MouseDoubleClickEvent, value); }
            remove { RemoveHandler (MouseDoubleClickEvent, value); }
        }

        /// <summary>
        /// Occurs when [balloon tip clicked].
        /// </summary>
        public event EventHandler BalloonTipClicked
        {
            add { AddHandler (BalloonTipClickedEvent, value); }
            remove { RemoveHandler (BalloonTipClickedEvent, value); }
        }

        /// <summary>
        /// Gets or sets the balloon tip title.
        /// </summary>
        /// <value>The balloon tip title.</value>
        public string BalloonTipTitle
        {
            get { return (string)GetValue (BalloonTipTitleProperty); }
            set { SetValue (BalloonTipTitleProperty, value); }
        }

        /// <summary>
        /// Gets or sets the balloon tip text format.
        /// </summary>
        /// <value>The balloon tip text format.</value>
        public string BalloonTipTextFormat
        {
            get { return (string)GetValue (BalloonTipTextFormatProperty); }
            set { SetValue (BalloonTipTextFormatProperty, value); }
        }

        /// <summary>
        /// Gets or sets the balloon tip timeout.
        /// </summary>
        /// <value>The balloon tip timeout.</value>
        public int BalloonTipTimeout
        {
            get { return (int)GetValue (BalloonTipTimeoutProperty); }
            set { SetValue (BalloonTipTimeoutProperty, value); }
        }

        /// <summary>
        /// Shows the balloon.
        /// </summary>
        /// <param name="args">The args.</param>
        public void ShowBalloon (params object[] args)
        {
            _notifyIcon.ShowBalloonTip (BalloonTipTimeout, BalloonTipTitle, string.Format (BalloonTipTextFormat, args), System.Windows.Forms.ToolTipIcon.None);
        }

        #region Conversion members

        private static Drawing.Icon FromImageSource (ImageSource icon)
        {
            if (icon == null)
            {
                return null;
            }
            Uri iconUri = new Uri (icon.ToString ());
            return new Drawing.Icon (Application.GetResourceStream (iconUri).Stream);
        }

        private static bool FromVisibility (Visibility visibility)
        {
            return visibility == Visibility.Visible;
        }

        private MouseButton ToMouseButton (Forms.MouseButtons button)
        {
            switch (button)
            {
                case Forms.MouseButtons.Left:
                    return MouseButton.Left;
                case Forms.MouseButtons.Right:
                    return MouseButton.Right;
                case Forms.MouseButtons.Middle:
                    return MouseButton.Middle;
                case Forms.MouseButtons.XButton1:
                    return MouseButton.XButton1;
                case Forms.MouseButtons.XButton2:
                    return MouseButton.XButton2;
            }
            throw new InvalidOperationException ();
        }

        #endregion
    }
}